/*
 * Copyright 2011 Kim Lindhardt Madsen
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package dk.lindhardt.gwt.geie.client.edit;

import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.HTMLTable;
import dk.lindhardt.gwt.geie.client.ElementStyler;
import dk.lindhardt.gwt.geie.client.TableMouseEventHandler;
import dk.lindhardt.gwt.geie.client.evaluate.GeieFormulaCalculator;
import dk.lindhardt.gwt.geie.shared.*;

import java.util.Arrays;
import java.util.List;

/**
 * User: AnAmuser
 * Date: 15-08-11
 * <p/>
 * When the user clicks a cell in the table the rich editor popup is shown. When the user confirms editing the
 * table is updated. The rebuilding recalculates all formulas and updates dirty cells.
 */
public class HTMLTablePresenterImpl implements HTMLTablePresenter {

   private TableLayout layout;
   private HTMLTable table;
   private ElementStyler styler;
   private TableMouseEventHandler mouseEventHandler;

   private GUICellEditor currentEditor;
   private Integer lastEditorLeft;
   private Integer lastEditorTop;

   /**
    * New DefaultHTMLTableEditHandler. When the user clicks a cell in the table the rich editor popup is shown.
    * When the user confirms editing the table is updated. The rebuilding recalculates all formulas and updates
    * dirty cells.
    */
   public HTMLTablePresenterImpl() {
   }

   public void setContext(TableLayout layout, HTMLTable table, ElementStyler styler, TableMouseEventHandler mouseEventHandler) {
      this.layout = layout;
      this.table = table;
      this.styler = styler;
      this.mouseEventHandler = mouseEventHandler;
   }

   public void onTableClick(int row, int column) {
      Cell layoutCell = layout.getCell(row, column, PositioningType.HTML);
      if (currentEditor == null) {
         if (!layoutCell.isLocked()) {
            layoutCell.setDirty(true);
            createAndShowEditor(Arrays.asList(layoutCell));
         }
      } else if (!layoutCell.equals(currentEditor.getEditedCell()) && currentEditor.isListeningForTableClicks()) {
         currentEditor.handleTableSelection(Arrays.asList(layoutCell));
      } else if (!layoutCell.equals(currentEditor.getEditedCell())) {
         resetEditor();
         createAndShowEditor(Arrays.asList(layoutCell));
      }

   }

   public void onTableRangeSelect(int topLeftRow, int topLeftColumn, int bottomRightRow, int bottomRightColumn) {
      List<Cell> cells = layout.getRange(
            new Position(topLeftRow, topLeftColumn),
            new Position(bottomRightRow, bottomRightColumn),
            PositioningType.HTML);

      if (currentEditor == null && cells.size() > 0) {
         Cell firstCell = cells.get(0);
         if (!firstCell.isLocked()) {
            firstCell.setDirty(true);
            createAndShowEditor(cells);
         }
      } else if (cells.size() > 0 && currentEditor.isListeningForTableClicks()) {
         currentEditor.handleTableSelection(cells);
      } else if (cells.size() > 0) {
         resetEditor();
         createAndShowEditor(cells);
      }
   }

   private void createAndShowEditor(List<Cell> layoutCells) {
      Cell cell = layoutCells.get(0);

      final ValueEditor.EditHandler valueEditHandler = getValueEditHandler();
      ValueEditor valueEditor = ValueEditorFactory.getValueEditor(valueEditHandler, cell);
      currentEditor = new GUICellEditor(layoutCells, valueEditor, valueEditHandler);

      Menu.MenuStateChangeHandler stateChangeHandler = getMenuStateChangeHandler(valueEditHandler);
      CellFormat cellFormat = layout.getCellFormat(cell.getCellFormatKey());
      currentEditor.addMenu(new TextStyleMenu(stateChangeHandler, cellFormat));
      currentEditor.addMenu(new AlignmentStyleMenu(stateChangeHandler, cellFormat));
      currentEditor.addMenu(new PatternStyleMenu(stateChangeHandler, cellFormat));
      currentEditor.addMenu(
            new TypeMenu(
                  stateChangeHandler,
                  ValueEditorFactory.getPossibleTypes(),
                  ValueEditorFactory.getTypeTextFromCell(cell)));

      if (lastEditorLeft != null && lastEditorTop != null) {
         currentEditor.setPopupPosition(lastEditorLeft, lastEditorTop);
         currentEditor.show();
      } else {
         currentEditor.center();
      }
   }

   private ValueEditor.EditHandler getValueEditHandler() {
      return new ValueEditor.EditHandler() {
            public void onEditConfirmed() {
               if (currentEditor.getValueEditor().validate()) {
                  editCellValue();
                  resetEditor();
               }
            }

            public void onEditCancelled() {
               resetEditor();
            }
         };
   }

   private Menu.MenuStateChangeHandler getMenuStateChangeHandler(final ValueEditor.EditHandler valueEditHandler) {
      return new Menu.MenuStateChangeHandler() {
            public void onStyleChanged() {
               editCellStyle();
            }

            public void onPatternChanged() {
               editCellStyle();
               if (currentEditor.getValueEditor().validate()) {
                  editCellValue();
               }
            }

            public void onTypeChanged(String type) {
               ValueEditor editor = ValueEditorFactory.getValueEditorFromTypeText(
                     valueEditHandler, currentEditor.getEditedCell(), type, "");
               currentEditor.setValueEditor(editor);
               currentEditor.redrawContent();
            }
         };
   }

   public void editCellStyle() {
      Cell cell = currentEditor.getEditedCell();
      CellFormat updatedCellFormat = layout.getCellFormat(cell.getCellFormatKey()).copy();
      for (Menu menu : currentEditor.getStyleMenus()) {
         if (menu instanceof StyleMenu) {
            ((StyleMenu) menu).transferValues(updatedCellFormat);
         }
      }

      CellFormatKey formatKey = layout.addCellFormat(updatedCellFormat.toString(), updatedCellFormat);
      List<Cell> cells = currentEditor.getCells();
      for (Cell c : cells) {
         c.setCellFormatKey(formatKey);
         Position position = c.getPosition();
         ColumnCorrectionBookKeeper bookKeeper = layout.getColumnCorrectionBookKeeper();
         Element element = table.getCellFormatter().getElement(
               position.getRow(),
               position.getColumn() - bookKeeper.getColumnCorrection(position, PositioningType.EXCEL));
         if (styler != null) {
            styler.styleElement(element, updatedCellFormat);
         }
      }
   }

   public void editCellValue() {
      Cell newCell = currentEditor.getValueEditor().createNewCell();
      newCell.setDirty(true);
      layout.replaceCell(currentEditor.getEditedCell(), newCell);

      GeieFormulaCalculator formulaCalculator = new GeieFormulaCalculator(layout);
      formulaCalculator.calculateAllFormulas();

      List<Cell> cells = layout.getCells();
      for (Cell cell : cells) {
         if (cell.isDirty()) {
            Position position = cell.getPosition();
            CellFormat cellFormat = layout.getCellFormat(cell.getCellFormatKey());
            int row = position.getRow();
            int column = position.getColumn();
            ColumnCorrectionBookKeeper bookKeeper = layout.getColumnCorrectionBookKeeper();
            table.setWidget(
                  row,
                  column - bookKeeper.getColumnCorrection(position, PositioningType.EXCEL),
                  cell.toWidget(cellFormat));
            cell.setDirty(false);
         }
      }

      currentEditor.setEditedCell(newCell);
   }

   private void resetEditor() {
      mouseEventHandler.resetMarking();
      lastEditorLeft = currentEditor.getAbsoluteLeft();
      lastEditorTop = currentEditor.getAbsoluteTop();
      currentEditor.hide();
      currentEditor = null;
   }
}
